#include "map.h"
#include <cmath>
#include <QDebug>

namespace kernel {

QString Map::getStationName(int stationNumber) const {
    return stationNameHash_.value(stationNumber, QString());
}

StationPoint* Map::findStationPoint(const QString& name) {
    if (stationIndexHash_.contains(name)) {
        int index = stationIndexHash_.value(name);
        if (index >= 0 && index < stationPoints_.size()) {
            return &stationPoints_[index];
        }
    }
    return nullptr;
}

const StationPoint* Map::findStationPoint(const QString& name) const {
    if (stationIndexHash_.contains(name)) {
        int index = stationIndexHash_.value(name);
        if (index >= 0 && index < stationPoints_.size()) {
            return &stationPoints_[index];
        }
    }
    return nullptr;
}

BezierPath* Map::findBezierPath(const QString& pathName) {
    if (bezierPathIndexHash_.contains(pathName)) {
        int index = bezierPathIndexHash_.value(pathName);
        if (index >= 0 && index < bezierPaths_.size()) {
            return &bezierPaths_[index];
        }
    }
    return nullptr;
}

double Map::calculateBezierLength(const BezierPath& path, int pointCount) const {
    double length = 0.0;
    Position lastPoint = calculateBezierPoint(0.0, path);

    for (int i = 1; i <= pointCount; ++i) {
        double t = static_cast<double>(i) / pointCount;
        Position currentPoint = calculateBezierPoint(t, path);

        double dx = currentPoint.x - lastPoint.x;
        double dy = currentPoint.y - lastPoint.y;
        length += std::sqrt(dx * dx + dy * dy);

        lastPoint = currentPoint;
    }

    return length;
}

Position Map::calculateBezierPoint(double t, const BezierPath& path) const {
    return calculateBezierPoint(t, path.startPoint.position, path.controlPoint1,
                               path.controlPoint2, path.endPoint.position);
}

Position Map::calculateBezierPoint(double t, const Position& p0, const Position& p1,
                                  const Position& p2, const Position& p3) const {
    double u = 1.0 - t;
    double tt = t * t;
    double uu = u * u;
    double uuu = uu * u;
    double ttt = tt * t;

    Position result;
    result.x = uuu * p0.x + 3 * uu * t * p1.x + 3 * u * tt * p2.x + ttt * p3.x;
    result.y = uuu * p0.y + 3 * uu * t * p1.y + 3 * u * tt * p2.y + ttt * p3.y;

    return result;
}

void Map::addStationPoint(const StationPoint& point) {
    stationPoints_.append(point);
}

void Map::addBezierPath(const BezierPath& path) {
    BezierPath pathWithLength = path;
    pathWithLength.length = calculateBezierLength(pathWithLength);
    bezierPaths_.append(pathWithLength);
}

void Map::addArcPath(const ArcPath& path) {
    arcPaths_.append(path);
}

void Map::addStraightPath(const StraightPath& path) {
    straightPaths_.append(path);
}

bool Map::isValid() const {
    return !header_.mapName.isEmpty() &&
           header_.resolution > 0.0 &&
           !stationPoints_.isEmpty();
}

void Map::clear() {
    header_ = MapHeader();
    stationPoints_.clear();
    bezierPaths_.clear();
    arcPaths_.clear();
    straightPaths_.clear();
    normalPoints_.clear();
    areas_.clear();
    blockAreas_.clear();
    stationNameHash_.clear();
    stationIndexHash_.clear();
    bezierPathIndexHash_.clear();
}

void Map::buildIndexes() {
    stationNameHash_.clear();
    stationIndexHash_.clear();
    bezierPathIndexHash_.clear();

    // Build station indexes
    for (int i = 0; i < stationPoints_.size(); ++i) {
        const auto& point = stationPoints_[i];
        stationIndexHash_.insert(point.instanceName, i);

        // Extract number from station name (e.g., "LM3" -> 3)
        QString numStr = point.instanceName.mid(2);
        bool ok;
        int stationNumber = numStr.toInt(&ok);
        if (ok) {
            stationNameHash_.insert(stationNumber, point.instanceName);
        }
    }

    // Build bezier path indexes
    for (int i = 0; i < bezierPaths_.size(); ++i) {
        const auto& path = bezierPaths_[i];
        QString pathName = path.instanceName;
        pathName.remove("LM"); // Remove "LM" prefix
        bezierPathIndexHash_.insert(pathName, i);
    }
}

} // namespace kernel